跳到主要内容

[toc]

nginx rewrite

本文部分抄袭于此

nginx rewrite官方文档

1.rewrite基本概述

1.1 什么是rewrite

rewrite即URL重写, 主要实现url地址重写以及重定向, 就是把传入Web的请求重定向到其他URL的过程。

1.2 rewrite使用场景

1.URL地址跳转,例如用户访问aaa.com将其跳转到bbb.com , 或者当用户通过http的方式访问aaa.com时,将其跳转至https的方式访问bbb.com

2.URL伪静态, 将动态页面显示为静态页面方式的一种技术, 便于搜索引擎的录入, 同时减少动态URL地址对外暴露过多的参数, 提升更高的安全性。

3.搜索引擎SEO优化依赖于url路径, 以便支持搜索引擎录入

2.rewrite配置语法

Syntax:	rewrite regex replacement [flag];
Default: —
Context: server, location, if

在匹配过程中可以引用一些Nginx的全局变量

变量说明
$document_root针对当前请求的根路径设置值
$host请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名
$request_filename当前请求的文件路径名(带网站的主目录/code/images/test.jpg)
$request_uri当前请求的文件路径名(不带网站的主目录/images/test.jpg)
$scheme请求用的协议,比如http或者https

rewrite匹配优先级

1.执行server块的rewrite指令

2.执行location匹配

3.执行选定的location中的rewrite

3.flag

rewrite指令根据表达式来重定向URI, 或者修改字符串。 可以应用于server、location、if环境下,每行rewrite指令最后跟一个flag标记,支持的flag标记有如下表格所示

flag说明
last本条规则匹配完成后,停止匹配,不在匹配后面的规则
break本条规则匹配完成后,停止匹配,不在匹配后面的规则
redirect返回302临时重定向, 地址栏会显示跳转后的地址
permanent返回301永久重定向,地址栏会显示跳转后的地址

last

官方解释

停止处理当前ngx_http_rewrite_module指令集, 并开始搜索与更改后的URI相匹配的新位置

如果匹配的URI,rewrite在server块中,并且last做为flag,匹配到此rewrite URI时,不再向下匹配server块中的rewrite,进而继续下面location URI的查找匹配;

如果匹配的URI,rewrite在location块中,last做为flag,匹配到此rewrite URI时,会跳出此location块,继续从上到下查找其它的location块URI,但不会再匹配server块中的rewrite中的URI

break

官方解释

ngx_http_rewrite_modulebreak指令一样, 停止处理当前的指令集

如果匹配的URI,rewrite在server块中,并且break做为flag,匹配到此rewrite URI时,不再向下匹配server块中的rewrite,进而继续下面location URI的查找匹配;

如果匹配的URI,rewrite在location块中,break做为flag,匹配到此rewrite时,不会跳出此location块,而是继续对location块下面的语句继续运行,不会跳出此location块,并且也不会匹配location 块下面的其它rewrite规则;

完成该rewrite规则的执行后,停止处理后续rewrite指令集,并不再重新查找;但是当前location内剩余非rewrite语句和location外的的非rewrite语句可以执行

redirect

官方解释

返回带有302代码的临时重定向;如果替换字符串不是以 "http://"," https://"或" $scheme" 开头,则使用该字符串

permanent

官方解释

返回301永久重定向,地址栏会显示跳转后的地址,即表示如果客户端不清理浏览器缓存,那么返回的结果将永久保存在客户端浏览器中了

4.nginx处理http请求的11个阶段

  • ngx_http_post_read_phase

    • 读取请求头,接收到http头部后处理阶段
  • ngx_http_server_rewrite_phase

    • 执行server块中rewrite,独立http阶段
  • ngx_http_find_config_phase

    • 根据uri查找替换location,uri寻找匹配的location阶段
  • ngx_http_rewrite_phase

    • 根据替换结果,继续执行rewrite,寻找到匹配的location之后再修改请求的uri
  • ngx_http_post_rewrite_phase

    • 执行rewrite后处理,在rewrite重写url后,防止错误的nginx.conf配置导致死循环(递归的修改uri)
  • ngx_http_preaccess_phase

    • 认证预处理,请求限制,连接限制,表示在处理ngx_http_access_phase阶段请求访问限制前,http模块可以介入处理的阶段
  • ngx_http_access_phase

    • 认证处理,用于让http模块判断是否允许这个请求访问nginx服务
  • ngx_http_post_access_phase

    • 认证后处理,认证不通过,丢包,在ngx_http_access_phase阶段中,当http模块的handler处理函数返回不允许访问的错误码时(ngx_htp_forbidden或者ngx_http_unauhorized),这里将负责向用户发送拒绝服务的错误相应
  • ngx_http_try_files_phase

    • 尝试try标签,此阶段专门为try_files配置预设立,当http请求访问静态资源时,try_files配置项可以使这个配置顺序的访问多个静态资源
  • ngx_http_content_phase

    • 内容处理,用于处理http请求内容的阶段,这是大部分http模块最愿意介入的阶段
  • ngx_http_log_phase

    • 日志处理,处理完请求后记录日志的阶段

5.rewrite配置实例

5.1 无flag测试

在nginx中引入echo、sleep、time等功能 github地址

无flag测试用例1

server {
listen 80;
rewrite ^/(.*)$ /nginx_one/$1;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/abc
This is nginx_two location
uri: /nginx_two/abc

无 flag 时,rewrite 会依次向下匹配,根据nginx在http请求处理的阶段中,我们 会先匹配server块中的rewrite规则;

第一次匹配rewrite ^/(.*)$ /nginx_one/$1; URI变成:/nginx_one/abc,无flag继续向下匹配;

第二次匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1; URI变成/nginx_two/abc; 再向下FIND_CONFIG阶段,查找location进行匹配,正好找到location /nginx_two/ 所以 response 如上;

无flag测试用例2

server {
listen 80;

rewrite ^/(.*)$ /nginx_one/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/abc
This is nginx_two location
uri: /nginx_two/abc

无flag,处理server块阶段,匹配rewrite ^/(.*)$ /nginx_one/$1 ,URI为:/nginx_one/abc; 到FIND_CONFIG阶段,匹配location, location /nginx_one/ ,这个location块中有rewrite再次匹配^/nginx_one/(.*)$ /nginx_two/$1; URI变为:/nginx_two/abc,这里没有flag,会跳出继续FIND_CONFIG阶段,而不会到 SERVER_REWRITE 阶段;而是匹配location /nginx_two/ ,所以response如上。

5.2 有flag测试

提示

redirect和permanent的区别就是 redirect 是临时重定向302,而permanent 是永久重定向301

5.2.1 flag redirect测试用例1

server {
listen 80;
#rewrite ^/(.*)$ /nginx_one/$1 redirect;
rewrite ^/(.*)$ https://www.baidu.com/ redirect;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}

测试结果

浏览器访问 虚拟机IP地址/abc 会跳转到百度首页 状态码为临时重定向302

这里的flag是redirect,说明需要重定向到replacement,正好这里的replacement有"https://",此时会直接跳转并返回给客户端;

如果打开#rewrite ^/(.*)$ /nginx_one/$1 redirect;的注释,浏览器中访问会提示重定向次数过多

5.2.2 flag redirect测试用例2

server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1 redirect;
rewrite ^/(.*)$ /nginx_one/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}

测试结果

$ curl -I 127.0.0.1/nginx_one/abc
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 22 Jun 2020 12:05:16 GMT
Content-Type: text/html
Content-Length: 145
Location: http://127.0.0.1/nginx_three/abc
Connection: keep-alive

1.匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1; 后,URI变为:/nginx_two/abc ; 无flag,继续向下;

2.继续匹配 rewrite ^/nginx_two/(.*)$ /nginx_three/$1 redirect; 302临时 重定向,URI: http://127.0.0.1/nginx_three/abc,

3.再次访问,此时会从SERVER_REWRITE这个阶段开始,此时匹配的是 rewrite ^/(.*)$ /nginx_one/$1; URI变为:/nginx_one/nginx_three/abc ,无flag,向 > 下FIND_CONFIG阶段;

4.最后查看location 匹配location /nginx_one/ ,所以回显如上;

5.2.3 flag last测试用例1

server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1 last;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/nginx_one/abc
This is nginx_two location
uri: /nginx_two/abc

1.匹配 rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1 last;遇到last,停止同级段的匹配,这里的意思是,中止server段向下的匹配,进行FIND_CONFIG阶段,URI变为:/k8svip_two/abc;

这里可以看出,如果last没有中止server段向下的匹配,会匹配rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1,实际结果是没有匹配的;

2.由上面步骤之后,匹配location /k8svip_two/, 所以会出现上面的response结果;

5.2.4 flag last测试用例2

server {
listen 80;
rewrite_log on;

rewrite ^/(.*)$ /nginx_one/$1;
rewrite ^/nginx_three/(.*)$ /nginx_four/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
rewrite ^/(.*)$ /nginx_three/$1 last;
rewrite ^/nginx_three/(.*)$ /;

echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}

location /nginx_four/ {
echo "This is nginx_four location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/abc
This is nginx_three location
uri: /nginx_three/nginx_one/abc

1.匹配rewrite ^/(.*)$ /k8svip_one/$1; URI变为:/nginx_one/abc;

2.匹配location /nginx_one/ 进而匹配location块中的rewrite ^/(.*)$ /nginx_three/$1 last; 因为是last,会跳出location继续FIND_CONFIG阶段

3.URI为:/nginx_three/nginx_one/abc;而不会到SERVER_WRITE阶段; 匹配 location /nginx_three/ ,所以看到上面的response;

5.2.5 flag break测试用例1

server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1 break;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;

location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/nginx_one/abc
This is nginx_two location
uri: /nginx_two/abc

1.匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1 break; flag为break,结束本层级的rewirte匹配,URI变为:/nginx_two/abc

2.继续FIND_CONFIG阶段,匹配location /nginx_two/; 所以response 如上;

5.2.6 flag break测试用例2

server {
listen 80;
rewrite_log on;

rewrite ^/(.*)$ /nginx_one/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}

location /nginx_one/ {
rewrite ^/(.*)$ /nginx_three/$1 break;
rewrite ^/nginx_two/(.*)$ /;

echo "This is nginx_one location";
echo "uri: ${uri}";
}

location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}

location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}

测试结果

$ curl 127.0.0.1/abc
This is nginx_one location
uri: /nginx_three/nginx_one/abc

1.匹配rewrite ^/(.*)$ /nginx_one/$1; URI变为:/nginx_one/abc

2.匹配location /nginx_one/,然后继续 rewrite ^/(.)$ /nginx_three/$1 break; flag为break,结束本层级的rewrite ^/(.)$ /nginx_three/$1 break;,并且继续进行本层级的其它操作;

3.此时的URI:/nginx_three/nginx_one/abc,所以response如上;